package IC;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import java_cup.runtime.Symbol;
import IC.AST.ASTNode;
import IC.AST.ICClass;
import IC.AST.PrettyPrinter;
import IC.AST.Program;
import IC.Parser.Lexer;
import IC.Parser.LibraryParser;
import IC.Parser.Parser;
import IC.Parser.SyntaxError;

public class Compiler
{
	private enum Type{
		library,
		IC
	}

	public static void main(String[] args) {
		ArgumentHandler argsHandler = new ArgumentHandler(args);
		
		//checks if the program called with the correct usage
		if (argsHandler.hasError()){
			System.out.println(argsHandler.getUsageMessage());
			return;
		}
		
		//checks if the the ICI file path supplied is valid
		if (!argsHandler.checkICFile()){
			System.out.println("The IC File " + argsHandler.getICFilePath() + " doesn't exist");
			return;
		}
		//checks if the library.sig supplied, and if so - valid
		if (argsHandler.hasLibraryFlag() && !argsHandler.checkLibraryFile()){
			System.out.println("The library file " + argsHandler.getICFilePath() + " doesn't exist");
			return;
		}
		
		//parse the library file - if flagged
		ICClass rootLibrary = null;
		if (argsHandler.hasLibraryFlag()){
			rootLibrary = parseLibrary(argsHandler.getLibraryFilePath());
			if (rootLibrary == null){
				return;
			}
			if (argsHandler.hasPrintAstFlag()){
				printAST(rootLibrary, argsHandler.getLibraryFilePath());
			}
		}
		

		//parse the IC program file supplied
		Program rootIC = parseIC(argsHandler.getICFilePath());
		if (rootIC == null){
			return;
		}
		
		//print the AST using prettyPrinter - if flagged
		if (argsHandler.hasPrintAstFlag()){
			if (argsHandler.hasDebugFlag()){
				BufferedWriter outFile = null;
				try {
					outFile = new BufferedWriter(new FileWriter(getPathOutputFile(argsHandler.getICFilePath())));
					outFile.write("Parsed " + argsHandler.getICFilePath() + " successfully!" + "\r\n" + getASTString(rootIC, argsHandler.getICFilePath()));
				} catch (IOException e) {
					System.out.println("Error: " + e.getMessage());
				} finally{
					if (outFile != null){
						try{
							outFile.close();
						} catch (IOException e) {
						System.out.println("Error: " + e.getMessage());
						}
					}
				}
			} else {
				printAST(rootIC, argsHandler.getICFilePath());
			}
		}
	}
	
	//prints the AST for the given root
	private static void printAST(ASTNode root, String path){
		System.out.println(getASTString(root, path));
	}
	
	//returns the string represtation of the AST given by the root
	private static String getASTString(ASTNode root, String path){
		PrettyPrinter printer = new PrettyPrinter(path);
		return (String) root.accept(printer);
	}

	//parse the library.sig file
	private static ICClass parseLibrary(String libraryFile){
		return (ICClass) parseFile(libraryFile, Type.library);
	}
	
	//parse the IC program file
	private static Program parseIC(String icFile){
		return (Program) parseFile(icFile, Type.IC);
	}

	//parse an IC file
	private static ASTNode parseFile(String path, Type type){
		FileReader txtFile = null;
		Lexer scanner = new Lexer(txtFile);
		Symbol parseResult = null;
		try{ 
			txtFile = new FileReader(path);
			scanner = new Lexer(txtFile);
			if (type == Type.library){
				LibraryParser libraryParser = new LibraryParser(scanner);
				parseResult = libraryParser.parse();

			} else{
				Parser icParser = new Parser(scanner);
				parseResult = icParser.parse();
			}
			System.out.println("Parsed " + path + " successfully!");
			if (txtFile != null){
				txtFile.close();
			}
			
		} catch (SyntaxError e){
			System.err.println(e);
		} catch (IOException e){
			System.err.println("Error: " + e);
		} catch (Exception e){
			System.err.println("Error: " + e);
		}
		
		if (parseResult == null){
			return null;
		}
		else {
			return (ASTNode) parseResult.value;
		}
	}
	
	//return the path for the output file (in debug mode)
	private static String getPathOutputFile(String path) throws IOException{
		File file = new File(path);
		return file.getParent() + File.separatorChar + "our" + File.separatorChar + getFileName(file) + ".ast.txt";	
	}
	
	//returns the name of the file supplied (without the extention
	private static String getFileName(File file){
		String nameWithExt = file.getName();
		int index = nameWithExt.lastIndexOf(".");
		return nameWithExt.substring(0,index);
	}

}